package com.shm.leetcode;
/**
 * 173. 二叉搜索树迭代器
 * 实现一个二叉搜索树迭代器类BSTIterator ，表示一个按中序遍历二叉搜索树（BST）的迭代器：
 * BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字，且该数字小于 BST 中的任何元素。
 * boolean hasNext() 如果向指针右侧遍历存在数字，则返回 true ；否则返回 false 。
 * int next()将指针向右移动，然后返回指针处的数字。
 * 注意，指针初始化为一个不存在于 BST 中的数字，所以对 next() 的首次调用将返回 BST 中的最小元素。
 *
 * 你可以假设 next() 调用总是有效的，也就是说，当调用 next() 时，BST 的中序遍历中至少存在一个下一个数字。
 *
 * 示例：
 *
 * 输入
 * ["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
 * [[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
 * 输出
 * [null, 3, 7, true, 9, true, 15, true, 20, false]
 *
 * 解释
 * BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
 * bSTIterator.next();    // 返回 3
 * bSTIterator.next();    // 返回 7
 * bSTIterator.hasNext(); // 返回 True
 * bSTIterator.next();    // 返回 9
 * bSTIterator.hasNext(); // 返回 True
 * bSTIterator.next();    // 返回 15
 * bSTIterator.hasNext(); // 返回 True
 * bSTIterator.next();    // 返回 20
 * bSTIterator.hasNext(); // 返回 False
 *
 *
 * 提示：
 *
 * 树中节点的数目在范围 [1, 105] 内
 * 0 <= Node.val <= 106
 * 最多调用 105 次 hasNext 和 next 操作
 *
 *
 * 进阶：
 *
 * 你可以设计一个满足下述条件的解决方案吗？next() 和 hasNext() 操作均摊时间复杂度为 O(1) ，并使用 O(h) 内存。其中 h 是树的高度。
 *
 */
import java.util.*;

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class BSTIterator {
    List<Integer> list = new ArrayList<>();

    /**
     * 方法一：扁平化
     * 我们可以直接对二叉搜索树做一次完全的递归遍历，获取中序遍历的全部结果并保存在数组中。随后，我们利用得到的数组本身来实现迭代器。
     * 复杂度分析
     *
     * 时间复杂度：初始化需要 O(n)O(n) 的时间，其中 nn 为树中节点的数量。随后每次调用只需要 O(1)O(1) 的时间。
     *
     * 空间复杂度：O(n)O(n)，因为需要保存中序遍历的全部结果。
     *
     * 作者：LeetCode-Solution
     * 链接：https://leetcode-cn.com/problems/binary-search-tree-iterator/solution/er-cha-sou-suo-shu-die-dai-qi-by-leetcod-4y0y/
     * @param root
     */
    public BSTIterator(TreeNode root) {
        dfs(root);
    }
    void dfs(TreeNode root){
        if(root==null){
            return;
        }
        if(root.left!=null){
            dfs(root.left);
        }
        list.add(root.val);
        if(root.right!=null){
            dfs(root.right);
        }
    }
    public int next() {
       Iterator<Integer> it= list.iterator();
       if(it.hasNext()){
           Integer ans = it.next();
           it.remove();
           return ans;
       }
       return -1;
    }
    
    public boolean hasNext() {
        Iterator<Integer> it= list.iterator();
        return it.hasNext();
    }

    /**
     * 方法二：迭代
     * 除了递归的方法外，我们还可以利用栈这一数据结构，通过迭代的方式对二叉树做中序遍历。此时，我们无需预先计算出中序遍历的全部结果，只需要实时维护当前栈的情况即可。
     *
     * 复杂度分析
     *
     * 时间复杂度：显然，初始化和调用 \text{hasNext()}hasNext() 都只需要 O(1)O(1) 的时间。每次调用 \text{next()}next() 函数最坏情况下需要 O(n)O(n) 的时间；但考虑到 nn 次调用 \text{next()}next() 函数总共会遍历全部的 nn 个节点，因此总的时间复杂度为 O(n)O(n)，因此单次调用平均下来的均摊复杂度为 O(1)O(1)。
     *
     * 空间复杂度：O(n)O(n)，其中 nn 是二叉树的节点数量。空间复杂度取决于栈深度，而栈深度在二叉树为一条链的情况下会达到 O(n)O(n) 的级别。
     *
     * 作者：LeetCode-Solution
     * 链接：https://leetcode-cn.com/problems/binary-search-tree-iterator/solution/er-cha-sou-suo-shu-die-dai-qi-by-leetcod-4y0y/
     */
    class BSTIteratorI {
        TreeNode cur;
        Deque<TreeNode> deque;
        public BSTIteratorI(TreeNode root) {
            cur=root;
            deque = new LinkedList<>();
        }

        public int next() {
            while(cur!=null){
                deque.push(cur);
                cur=cur.left;
            }
            cur = deque.pop();
            int ans = cur.val;
            cur = cur.right;
            return ans;
        }

        public boolean hasNext() {
            return cur!=null||!deque.isEmpty();
        }
    }

}

/**
 * Your BSTIterator object will be instantiated and called as such:
 * BSTIterator obj = new BSTIterator(root);
 * int param_1 = obj.next();
 * boolean param_2 = obj.hasNext();
 */